from typing import List
from xq_factor import set_option, add_api
from xq_factor.factor import LeafFactor, ObjectedLeafFactor, AbstractFactor
from xq_factor.operator import PROPORTION, RANK
from xq_factor.analyzer.base import AbstractAnalyzer
from xqdata.constant import SecuType
from xqdata.api import DataApi
from xqdata_ddb import DDBDataApi
from xqdata_rq import RQDataApi
from xqdata_rq.func import _all_instruments
from gypb.db import get_db, get_ddbapi
import dolphindb as ddb
import pandas as pd
import os
import rqdatac
import plotly.express as px

def get_prod_market_value(
    prodcode: str,
    start_date: pd.Timestamp,
    end_date: pd.Timestamp,
) -> pd.DataFrame:
    ddbapi = get_ddbapi()
    info = ddbapi.get_info("prodcode_account").query(f"prod_code == {prodcode}")
    market_value = sum(
        [
            ObjectedLeafFactor("market_value", account)
            for account in info["account"].to_list()
        ]
    )
    return market_value.value.loc[start_date:end_date]


def industry_concentration(market_value: AbstractFactor, industry: AbstractFactor):
    position_weight = PROPORTION(market_value)
    df = pd.concat(
        [position_weight.value.stack(), industry.value.stack()], axis=1
    ).dropna()
    df.columns = ["factor", "grouper"]
    ind_df = (
        df.groupby(["datetime", "grouper"], group_keys=False)["factor"]
        .sum()
        .unstack(level="grouper")
    )
    return ind_df


def riskfactor_exposure(market_value: AbstractFactor, riskfactors: List[LeafFactor]):
    position_weight = PROPORTION(market_value)
    factors = []
    for riskfactor in riskfactors:
        weighted_exposure = position_weight * riskfactor
        weighted_exposure = weighted_exposure.value.sum(axis=1)
        weighted_exposure.name = riskfactor.name
        factors.append(weighted_exposure)
    return pd.concat(factors, axis=1)


def holding_num(market_value: pd.DataFrame):
    return (market_value > 0).sum(axis=1)


def prods_holding_num(
    prod_codes: List[str],
    start_date: pd.Timestamp = None,
    end_date: pd.Timestamp = None,
) -> pd.DataFrame:
    holdings = {}
    for prod_code in prod_codes:
        market_value = get_prod_market_value(prod_code, start_date, end_date)
        prod_holding_num = holding_num(market_value)
        holdings[prod_code] = prod_holding_num
    return pd.DataFrame(holdings)

def prods_holding_figures(prods_holding_num:pd.DataFrame):
    fig = px.line(prods_holding_num)
    fig.update_layout(
        title='持仓数量随时间变化图',
        xaxis_title='时间',
        yaxis_title='持仓数量',
        legend_title='产品'
    )
    return fig

def holdings(
    market_value: AbstractFactor, top_n: int = None, date: pd.Timestamp = None
):
    position_weight = PROPORTION(market_value)

    holdings = market_value.value.stack().to_frame()
    holdings.columns = ["market_value"]
    holdings.sort_values(
        by=["datetime", "market_value"], ascending=[True, False], inplace=True
    )
    holdings["position_weight"] = position_weight.value.stack()
    if top_n is not None:
        weight_rank = RANK(market_value, ascending=False).value.stack()
        index = weight_rank[weight_rank <= top_n].index
        holdings = holdings.loc[index]
    if date is not None:
        holdings = holdings.loc[date]
    return holdings


# class PerformanceAnalyzer(AbstractAnalyzer):
#     def __init__(self, market_value: AbstractFactor, benchmark: AbstractFactor):
#         self.market_value = market_value
#         self.benchmark = benchmark

#     def process(self, factor):
#         return PerformanceAnalysisResult(self.market_value, self.benchmark, factor)

# class PerformanceAnalysisResult:

if __name__ == "__main__":
    from gypb.config import settings
    rqdatac.init(settings.rqdata.user,settings.rqdata.password)
    market_value = ObjectedLeafFactor("market_value", 1368803245)
    total_income = LeafFactor("total_income")
    close = LeafFactor("close_post")
    citics_2019_l1_name = LeafFactor("citics_2019_l1_name", api="rq")
    stocks = _all_instruments(type=SecuType.STOCK)
    set_option("universe", stocks.index)
    set_option("start_time", pd.Timestamp("2024-08-15"))
    set_option("end_time", pd.Timestamp("2024-12-15"))
    add_api(RQDataApi(), "rq")
    with get_db() as session:
        ddb_api = DDBDataApi()
        ddb_api.session = session
        add_api(ddb_api)
        print(
            holdings(
                market_value,
                3,
                [pd.Timestamp("2024-12-03"), pd.Timestamp("2024-12-06")],
            )
        )
        # industry_concentration(market_value, citics_2019_l1_name)
        # print(market_value.value.head(10))

        # print(total_income.value.head(10))
        # print(close.value.head(10))
        # print(citics_2019_l1_name.value.head(10))
